<?php

namespace app\common\library;

use app\common\model\Config;
use EasyWeChat\Factory;

/**
 * 微信登录模型
 */
class Wechat
{

    protected $app;
    protected $config;

    public function __construct($platform)
    {
        $this->setConfig($platform);

        switch ($platform) {
            case 'wxOfficialAccount':  //微信公众号
                $this->app = Factory::officialAccount($this->config);
                break;
            case 'wxMiniProgram':      //微信小程序(用户端)
                $this->app = Factory::miniProgram($this->config);
                break;
            case 'wxMiniProgram2':      //微信小程序(门店端/驿站端)
                $this->app = Factory::miniProgram($this->config);
                break;
            case 'wxAppProgram':      //微信app

                break;
        }
    }
    //微信app登录
    public static function wxAppLogin($code)
    {
        $appid =  'wxd34dfb096708edde';
        $secret =  '8460b3fa982de157b5fa49e25e2a0a92';
        $url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' . $appid . '&secret=' . $secret . '&code=' . $code . '&grant_type=authorization_code';
        $response =  file_get_contents($url);
        $callback = json_decode($response, true);
        return $callback;
    }
    // 返回实例
    public function getApp()
    {
        return $this->app;
    }

    //小程序:获取openid&session_key
    public function code($code)
    {
        return $this->app->auth->session($code);
    }

    public function oauth()
    {
        return $this->app->oauth;
    }

    //解密信息
    public function decryptData($session, $iv, $encryptData)
    {
        return $this->app->encryptor->decryptData($session, $iv, $encryptData);
    }

    public function unify($orderBody)
    {
        return $this->app->order->unify($orderBody);
    }

    public function bridgeConfig($prepayId)
    {
        $jssdk = $this->app->jssdk;
        $config = $jssdk->bridgeConfig($prepayId, false);
        return $config;
    }

    public function notify()
    {
        return $this->app;
    }

    //获取accessToken
    public function getAccessToken()
    {
        $accessToken = $this->app->access_token;
        $token = $accessToken->getToken(true); // token 数组 token['access_token'] 字符串
        return $token;
    }

    /**
     * 合并默认配置
     *
     * @param [type] $platform
     * @return void
     */
    private function setConfig($platform)
    {
        $debug = config('app_debug');
        $defaultConfig = [
            'app_id' => $platform == 'wxMiniProgram2' ? 'wx2113b8c1a69b6456' : 'wxd8f03c1d91d8e896',
            'secret' => $platform == 'wxMiniProgram2' ? '438272c5da8cfb67d2589abede63c9ac' : '991349cd3f6a38db2cb185559c45fca8',
            // 指定 API 调用返回结果的类型：array(default)/collection/object/raw/自定义类名
            'response_type' => 'array',
            'log' => [
                'default' => $debug ? 'dev' : 'prod', // 默认使用的 channel，生产环境可以改为下面的 prod
                'channels' => [
                    // 测试环境
                    'dev' => [
                        'driver' => 'single',
                        'path'   => ROOT_PATH . 'public/logs/wechat_login.log',
                        'level'  => 'debug',
                    ],
                    // 生产环境
                    'prod' => [
                        'driver' => 'daily',
                        'path'   => ROOT_PATH . 'public/logs/wechat_login.log',
                        'level'  => 'info',
                    ],
                ],
            ],
        ];

        $oauthConfig = [
            'oauth' => [
                'scopes'   => ['snsapi_userinfo'],
                'callback' => request()->domain() . '/api/User/wxOfficialAccountOauth'
            ],
        ];
        // 获取对应平台的配置
        // $this->config = Config::getEasyWechatConfig($platform);
        // 根据框架 debug 合并 log 配置
        $this->config = $defaultConfig;
        // 根据框架 平台 合并 oauth 配置
        if ($platform === 'wxOfficialAccount') {
            $this->config = array_merge($this->config, $oauthConfig);
        }
    }

    /**

     * @notes 商家转账到零钱

     * @param $batch_no //提现订单号

     * @param $left_money//提现金额 单位 元

     * @param $user_openid //用户openID

     * @param $withdraw_name //提现金额大于200,用户真实名字必填

     * @return bool

     * @throws \Exception

     */

    public static function transfer($role, $batch_no, $left_money, $user_openid, $withdraw_name = '')
    {
        $config = \think\Config::get('site');

        //请求URL

        $url = 'https://api.mch.weixin.qq.com/v3/transfer/batches';

        //请求方式

        $http_method = 'POST';

        //请求参数

        $appid = $role == 'user' ? $config['wx_appid'] : $config['app_id'];

        $data = [

            'appid' => $appid, //申请商户号的appid或商户号绑定的appid（企业号corpid即为此appid）

            'out_batch_no' => $batch_no, //商户系统内部的商家批次单号，要求此参数只能由数字、大小写字母组成，在商户系统内部唯一

            'batch_name' => '提现至微信零钱', //该笔批量转账的名称

            'batch_remark' => '提现至微信零钱', //转账说明，UTF8编码，最多允许32个字符

            'total_amount' => $left_money * 100, //转账金额单位为“分”。转账总金额必须与批次内所有明细转账金额之和保持一致，否则无法发起转账操作

            'total_num' => 1, //一个转账批次单最多发起三千笔转账。转账总笔数必须与批次内所有明细之和保持一致，否则无法发起转账操作

            'transfer_detail_list' => [

                [ //发起批量转账的明细列表，最多三千笔

                  'out_detail_no' => (string)($batch_no + 5), //商户系统内部区分转账批次单下不同转账明细单的唯一标识，要求此参数只能由数字、大小写字母组成

                  'transfer_amount' => $left_money * 100, //转账金额单位为分

                  'transfer_remark' => '提现至微信零钱', //单条转账备注（微信用户会收到该备注），UTF8编码，最多允许32个字符

                  'openid' => $user_openid, //openid是微信用户在公众号appid下的唯一用户标识（appid不同，则获取到的openid就不同），可用于永久标记一个用户

                ]

            ]

        ];

        if ($left_money >= 2000) {

            if (empty($withdraw_name)) {

                throw new \Exception('提现金额 >= 2000元，收款用户真实姓名必填');

            }

            $data['transfer_detail_list'][0]['user_name'] = self::getEncrypt($withdraw_name, $config);

        }

        $token = self::token($url, $http_method, $data, $config); //获取token

        $result = self::https_request($url, json_encode($data), $token); //发送请求

        $result_arr = json_decode($result, true);

        if (!isset($result_arr['create_time'])) { //批次受理失败

            throw new \Exception($result_arr['message']);

        }


        //      成功返回信息  {"batch_id":"1030001036201351072852022101201442513049","create_time":"2022-10-12T22:08:21+08:00","out_batch_no":"20221011004103000000146822"}
        // { ["batch_id"]=> string(42) "131000407018201480381782023082551202317731" ["batch_status"]=> string(8) "ACCEPTED" ["create_time"]=> string(25) "2023-08-25T10:45:36+08:00" ["out_batch_no"]=> string(10) "1692931496" }

        //批次受理成功，更新提现申请单为提现中状态

        //业务修改为提现中

        return $result_arr;

    }


    /**

     * @notes 商家明细单号查询明细单API

     * @param $withdrawApply

     * @param $config

     * @return mixed

     */

    public static function details($out_batch_no)

    {

        $config = \think\Config::get('site');

        $out_detail_no = $out_batch_no + 5;

        //请求URL

        $url = 'https://api.mch.weixin.qq.com/v3/transfer/batches/out-batch-no/' . $out_batch_no . '/details/out-detail-no/' . $out_detail_no;

        //请求方式

        $http_method = 'GET';

        //请求参数

        $data = [];

        $token = self::token($url, $http_method, $data, $config); //获取token

        $result = self::https_request($url, $data, $token); //发送请求

        $result_arr = json_decode($result, true);

        # - 返回值array(12) { ["appid"]=> string(18) "wx48baca009797518d" ["batch_id"]=> string(42) "131000110005501480381782023110328370897004" ["detail_id"]=> string(42) "132000110005501480381782023110344698517137" ["detail_status"]=> string(7) "SUCCESS" ["initiate_time"]=> string(25) "2023-11-03T11:19:34+08:00" ["mchid"]=> string(10) "1627693887" ["openid"]=> string(28) "oUJfh5VMmwVP9dOE8A8HvYBChOJ4" ["out_batch_no"]=> string(10) "1698981451" ["out_detail_no"]=> string(10) "1698981456" ["transfer_amount"]=> int(180) ["transfer_remark"]=> string(21) "提现至微信零钱" ["update_time"]=> string(25) "2023-11-03T11:19:40+08:00" }

        return $result_arr;

    }


    /**

     * @notes 签名生成

     * @param $url

     * @param $http_method

     * @param $data

     * @param $config

     * @return string

     */

    public static function token($url, $http_method, $data, $config)

    {

        $timestamp = time(); //请求时间戳

        $url_parts = parse_url($url); //获取请求的绝对URL

        $nonce = $timestamp . rand('10000', '99999'); //请求随机串

        $body = empty($data) ? '' : json_encode((object)$data); //请求报文主体

        $stream_opts = [

            "ssl" => [

                "verify_peer" => false,

                "verify_peer_name" => false,

            ]

        ];

        $apiclient_cert_arr = openssl_x509_parse(file_get_contents(APP_PATH . '/certs/apiclient_cert.pem', false, stream_context_create($stream_opts)));

        $serial_no = $apiclient_cert_arr['serialNumberHex']; //证书序列号

        $mch_private_key = file_get_contents(APP_PATH . '/certs/apiclient_key.pem', false, stream_context_create($stream_opts)); //密钥

        $merchant_id = $config['mch_id']; //商户id

        $canonical_url = ($url_parts['path'] . (!empty($url_parts['query']) ? "?${url_parts['query']}" : ""));

        $message = $http_method . "\n" .

            $canonical_url . "\n" .

            $timestamp . "\n" .

            $nonce . "\n" .

            $body . "\n";

        openssl_sign($message, $raw_sign, $mch_private_key, 'sha256WithRSAEncryption');

        $sign = base64_encode($raw_sign); //签名

        $schema = 'WECHATPAY2-SHA256-RSA2048';

        $token = sprintf(

            'mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',

            $merchant_id,

            $nonce,

            $timestamp,

            $serial_no,

            $sign

        ); //微信返回token

        return $schema . ' ' . $token;

    }

    /**

     * @notes 发送请求

     * @param $url

     * @param $data

     * @param $token

     * @return bool|string

     */

    public static function https_request($url, $data, $token)

    {

        $curl = curl_init();

        curl_setopt($curl, CURLOPT_URL, (string)$url);

        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);

        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);

        if (!empty($data)) {

            curl_setopt($curl, CURLOPT_POST, 1);

            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);

        }

        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);

        //添加请求头

        $headers = [

            'Authorization:' . $token,

            'Accept: application/json',

            'Content-Type: application/json; charset=utf-8',

            'User-Agent:Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',

        ];

        if (!empty($headers)) {

            curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);

        }

        $output = curl_exec($curl);

        curl_close($curl);

        return $output;

    }

    /**

     * @notes 敏感信息加解密

     * @param $str

     * @param $config

     * @return string

     * @throws \Exception

     */

    public static function getEncrypt($str, $config)

    {

        //$str是待加密字符串

        $public_key = file_get_contents($config['cert_client']);

        $encrypted = '';

        if (openssl_public_encrypt($str, $encrypted, $public_key, OPENSSL_PKCS1_OAEP_PADDING)) {

            //base64编码

            $sign = base64_encode($encrypted);

        } else {

            throw new \Exception('encrypt failed');

        }

        return $sign;

    }
}
